/*
 * CCMediaReader.cpp
 *
 *  Created on: 2016-8-13
 *      Author: zhengboyuan
 */

#include "CMediaReader.h"
#include "CMediaSourceFactory.h"
#include "FfmpegUtil.h"
#include "CFileMediaSink.h"
#include "CLog.h"
#include "Utility.h"
#include "MediaEvent.h"


namespace av
{


CMediaReader::CMediaReader():
	m_handle(-1),
	m_eventCallback(),
	m_eventContext(),
	m_endOfStream(false)
{
	m_factory.reset(new CMediaSourceFactory());
}

CMediaReader::~CMediaReader()
{
	close();
}

int CMediaReader::open(const std::string& url, const std::string& params)
{
	m_source.reset(m_factory->create(url, params), MediaSourceDeleter(m_factory));
	if (!m_source)
	{
		return ENODEV;
	}

	int ret = m_source->open(url, params);
	if (ret != 0)
	{
		m_source.reset();
		return ret;
	}

	m_source->getFormat(m_format);

	fireMediaFormat(m_format);
	fireMediaEvent(MEDIA_EVENT_FORMAT_READY, 0);
	
	return ret;
}

void CMediaReader::close()
{
	stopRecord();

	if (m_source)
	{
		m_source->close();
		m_source.reset();
	}
}

bool CMediaReader::isOpen()
{
	return (m_source && m_source->isOpen());
}

int CMediaReader::getFormat(MediaFormat& fmt)
{
	if (!m_source)
	{
		return ENODEV;
	}

	if (!m_source->getFormat(fmt))
	{
		return EAGAIN;
	}

	return 0;
}

int CMediaReader::getDuration()
{
	if (!m_source)
	{
		return false;
	}
	return m_source->getDuration();
}

int CMediaReader::play()
{
	if (!m_source)
	{
		return false;
	}

	return m_source->play();
}

int CMediaReader::pause()
{
	if (!m_source)
	{
		return false;
	}

	return m_source->pause();
}

void CMediaReader::stop()
{
	if (!m_source)
	{
		return ;
	}

	m_source->stop();

	if (m_fileSink)
	{
		m_fileSink->close();
	}
}


int CMediaReader::getState()
{
	if (!m_source)
	{
		return STATE_STOPPED;
	}

	return m_source->getState();
}


bool CMediaReader::seekable()
{
	if (!m_source)
	{
		return false;
	}

	return m_source->seekable();
}

int CMediaReader::seek(int64_t offset)
{
	if (!m_source)
	{
		return ENOENT;
	}

	return m_source->seek(offset);
}

int64_t CMediaReader::getTime()
{
	if (!m_source)
	{
		return 0;
	}

	return m_source->getTime();
}

int CMediaReader::setScale(float scale)
{
	if (!m_source)
	{
		return ENOENT;
	}

	return m_source->setScale(scale);
}


float CMediaReader::getScale()
{
	if (!m_source)
	{
		return 1.0;
	}

	return m_source->getScale();
}

int CMediaReader::read(AVPacketPtr& pkt)
{
	if (!m_source)
	{
		return ENOENT;
	}

	if (!pkt)
	{
		pkt.reset(new AVPacket(), ffmpeg::PacketDeleter());
	}
	av_init_packet(pkt.get());

	int ret = m_source->read(*pkt.get());
	if (ret == 0)
	{
		if (m_fileSink)
		{
			m_fileSink->onMediaPacket(pkt);
		}
	}
	return ret;
}


void CMediaReader::interrupt()
{
	if (!m_source)
	{
		return ;
	}

	m_source->interrupt();
}

bool CMediaReader::isLive()
{
	if (!m_source)
	{
		return false;
	}

	return m_source->isLive();
}


int  CMediaReader::startRecord(const std::string& filename)
{
	if (!m_source)
	{
		return ENODEV;
	}

	if (!m_fileSink)
	{
		m_fileSink.reset(new CFileMediaSink());

		MediaFormat fmt;
		m_source->getFormat(fmt);
		m_fileSink->onMediaFormat(fmt);
	}

	bool done = m_fileSink->open(filename.c_str());

	return done ? 0 : EIO;
}

void CMediaReader::stopRecord()
{
	if (m_fileSink)
	{
		m_fileSink->close();
		m_fileSink.reset();
	}
}

bool CMediaReader::isRecording()
{
	return (m_fileSink.get() != NULL);
}

int CMediaReader::startRender()
{
	return ENOSYS;
}

void CMediaReader::stopRender()
{

}

bool CMediaReader::isRendering()
{
	return false;
}

void CMediaReader::setVideoWindow(void* window)
{

}


void CMediaReader::setEventCallback(ReaderEventCallback cb, void* context)
{
	comn::AutoCritSec lock(m_cs);
	m_eventCallback = cb;
	m_eventContext = context;
}

void CMediaReader::setHandle(int handle)
{
	m_handle = handle;
}

bool CMediaReader::waitForReadable(int ms)
{
	return true;
}

int CMediaReader::step()
{
	return 0;
}

int CMediaReader::nextKeyFrame()
{
	return 0;
}

void CMediaReader::fireMediaFormat(const MediaFormat& fmt)
{
}

void CMediaReader::fireMediaEvent(int type, int64_t value)
{
	//if (type != MEDIA_EVENT_PACKET)
    CLog::info("media event. type:%d. name:%s\n", type, Utility::getEventName(type));

	{
		comn::AutoCritSec lock(m_cs);
		if (m_eventCallback)
		{
			(*m_eventCallback)(m_handle, type, value, m_eventContext);
		}
	}

	if (type == STREAM_EVENT_END)
	{
		comn::AutoCritSec lock(m_cs);
		if (m_fileSink)
		{
			m_fileSink->onMediaEvent(type);
		}

		m_endOfStream = true;
	}
	else if (type == MEDIA_EVENT_OPEN_FAILED)
	{
		comn::AutoCritSec lock(m_cs);
		m_endOfStream = true;
	}

}


}
